
#ifndef _AEVECTOR4FCOMPARISONSSE_INL_
#define _AEVECTOR4FCOMPARISONSSE_INL_

static AE_ALIGN16 const AEINT32 AEVector4fComparison_maskToComparison[16*4] = 
{
	0,  0, 0, 0,
	~0,  0, 0, 0,
	0, ~0, 0, 0,
	~0, ~0, 0, 0,
	0,  0,~0, 0,
	~0,  0,~0, 0,
	0, ~0,~0, 0,
	~0, ~0,~0, 0,
	0,  0, 0,~0,
	~0,  0, 0,~0,
	0, ~0, 0,~0,
	~0, ~0, 0,~0,
	0,  0,~0,~0,
	~0,  0,~0,~0,
	0, ~0,~0,~0,
	~0, ~0,~0,~0,
};

AE_FORCEINLINE const AEVector4fComparison AE_CALL AEVector4fComparison::Convert( const AEVector4fMask &vcMask )
{
	AEVector4fComparison c;
	c.m_mMask = vcMask;
	return c;
}

AE_FORCEINLINE void AEVector4fComparison::SetAnd( AEVector4fConstRefCompParam vcComp1, AEVector4fConstRefCompParam vcComp2 )
{ 
	m_mMask = _mm_and_ps( vcComp1.m_mMask, vcComp2.m_mMask ); 
}

AE_FORCEINLINE void AEVector4fComparison::SetAndNot( AEVector4fConstRefCompParam vcComp1, AEVector4fConstRefCompParam vcComp2 )
{
	m_mMask = _mm_andnot_ps( vcComp2.m_mMask, vcComp1.m_mMask );
}

AE_FORCEINLINE void AEVector4fComparison::SetXor( AEVector4fConstRefCompParam vcComp1, AEVector4fConstRefCompParam vcComp2 )
{
	m_mMask = _mm_xor_ps( vcComp1.m_mMask, vcComp2.m_mMask );
}

AE_FORCEINLINE void AEVector4fComparison::SetNot( AEVector4fConstRefCompParam vcComp )
{ 
	m_mMask = _mm_castsi128_ps( _mm_cmpeq_epi32( _mm_setzero_si128(), _mm_castps_si128( vcComp.m_mMask ) ) );
}

AE_FORCEINLINE void AEVector4fComparison::SetOr( AEVector4fConstRefCompParam vcComp1, AEVector4fConstRefCompParam vcComp2 )
{ 
	m_mMask = _mm_or_ps( vcComp1.m_mMask, vcComp2.m_mMask ); 
}

AE_FORCEINLINE void AEVector4fComparison::SetSelect( AEVector4fConstRefCompParam vcComp, AEVector4fConstRefCompParam vcTrueValue, AEVector4fConstRefCompParam vcFalseValue )
{
#if AE_SSE_VERSION >= 0x41
	m_mMask = _mm_blendv_ps( vcFalseValue.m_mMask, vcTrueValue.m_mMask, vcComp.m_mMask );
#else
	m_mMask = _mm_or_ps( _mm_and_ps( vcComp.m_mMask, vcTrueValue.m_mMask ), _mm_andnot_ps( vcComp.m_mMask, vcFalseValue.m_mMask ) );
#endif
}

template<> 
AE_FORCEINLINE void AEVector4fComparison::SetSelect<AEVector4ComparisonMask::MASK_X>( AEVector4fConstRefCompParam vcTrueValue, AEVector4fConstRefCompParam vcFalseValue )
{
	m_mMask = _mm_move_ss( vcFalseValue.m_mMask, vcTrueValue.m_mMask );
}

#if AE_SSE_VERSION >= 0x41
template<AEVector4ComparisonMask::Mask M> 
AE_FORCEINLINE void AEVector4fComparison::SetSelect( AEVector4fConstRefCompParam vcTrueValue, AEVector4fConstRefCompParam vcFalseValue )
{
	AE_VECTOR4FCOMPARISON_MASK_CHECK;
	m_mMask = _mm_blend_ps( vcFalseValue.m_mMask, vcTrueValue.m_mMask, M );
}
#else
template<> 
AE_FORCEINLINE void AEVector4fComparison::SetSelect<AEVector4ComparisonMask::MASK_XY>( AEVector4fConstRefCompParam vcTrueValue, AEVector4fConstRefCompParam vcFalseValue )
{
	m_mMask = _mm_shuffle_ps(vcTrueValue.m_mMask, vcFalseValue.m_mMask, _MM_SHUFFLE(3,2,1,0));
}

template<> 
AE_FORCEINLINE void AEVector4fComparison::SetSelect<AEVector4ComparisonMask::MASK_XYZ>( AEVector4fConstRefCompParam vcTrueValue, AEVector4fConstRefCompParam vcFalseValue )
{
	m_mMask = _mm_shuffle_ps( vcTrueValue.m_mMask, _mm_unpackhi_ps( vcTrueValue.m_mMask, vcFalseValue.m_mMask ), _MM_SHUFFLE( 3, 0, 1, 0 ) );
}

template <AEVector4ComparisonMask::Mask M>
AE_FORCEINLINE void AEVector4fComparison::SetSelect( AEVector4fConstRefCompParam vcTrueValue, AEVector4fConstRefCompParam vcFalseValue )
{
	AEVector4fComparison cmp;
	cmp.Set<M>();
	SetSelect( cmp, vcTrueValue, vcFalseValue );
}
#endif

AE_FORCEINLINE void AEVector4fComparison::Set( Mask mMask )
{ 
	// TODO: Sistemare meglio
	//AE_COMPILE_TIME_ASSERT( AEVector4ComparisonMask::INDEX_X == 0, TOBEDEFINED );
	//AE_COMPILE_TIME_ASSERT( AEVector4ComparisonMask::MASK_X == 1, "INDEX_X = 0" );
	//AE_COMPILE_TIME_ASSERT( AEVector4ComparisonMask::MASK_Y == 2, "INDEX_X = 0" );
	//AE_COMPILE_TIME_ASSERT( AEVector4ComparisonMask::MASK_XYZW == 15, "INDEX_X = 0" );

	AEASSERT_MSG( ( mMask & 0xf ) == mMask, "illegal mask value handed in");

	m_mMask = _mm_load_ps( (const AEFLOAT32*)&(AEVector4fComparison_maskToComparison[mMask*4]) );
}

template <AEVector4ComparisonMask::Mask M>
AE_FORCEINLINE void AEVector4fComparison::Set( void )
{ 
	AE_VECTOR4FCOMPARISON_MASK_CHECK;
	if( M == MASK_NONE )
	{
		m_mMask = _mm_setzero_ps();
	}
	else
	{
#if AE_SSE_VERSION >= 0x41
		const __m128 zeros = _mm_setzero_ps();
		const __m128 ones = _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_setzero_si128(),_mm_setzero_si128()));
		if( M == MASK_XYZW )
		{
			m_mMask = ones;
		}
		else
		{
			m_mMask = _mm_blend_ps( zeros, ones, M );
		}
#else
		m_mMask = _mm_load_ps( (const AEFLOAT32*)&(AEVector4fComparison_maskToComparison[M*4]) );
#endif
	}
}

template <AEVector4ComparisonMask::Mask M>
AE_FORCEINLINE AEBOOL32 AEVector4fComparison::AllAreSet( void ) const
{ 
	AE_VECTOR4FCOMPARISON_MASK_CHECK;
	if( M == MASK_NONE )
	{
		return true;
	}
	else
	{
#if AE_SSE_VERSION >= 0x41
		if( M == MASK_XYZW )
		{
			return _mm_test_all_ones( _mm_castps_si128( m_mMask ) );
		}
		else
		{
			const __m128 zeros = _mm_setzero_ps();
			const __m128 ones = _mm_castsi128_ps( _mm_cmpeq_epi32( _mm_setzero_si128(),_mm_setzero_si128() ) );
			__m128 comp = _mm_blend_ps( zeros, ones, M );
			__m128i mask = _mm_and_si128( _mm_castps_si128( m_mMask ), _mm_castps_si128( comp ) );
			return _mm_testc_si128( mask, _mm_castps_si128( comp ) );
		}
#else
		if( M == MASK_XYZW )
			return _mm_movemask_ps( m_mMask ) == MASK_XYZW; 
		else
			return ( _mm_movemask_ps( m_mMask ) & M ) == M; 
#endif
	}
}

template <AEVector4ComparisonMask::Mask M>
AE_FORCEINLINE AEBOOL32 AEVector4fComparison::AnyIsSet( void ) const
{ 
	AE_VECTOR4FCOMPARISON_MASK_CHECK;
	
	if( M == MASK_NONE )
		return false;
	else if( M == MASK_XYZW )
		return _mm_movemask_ps( m_mMask );
	else
		return _mm_movemask_ps( m_mMask ) & M; 
}

AE_FORCEINLINE AEBOOL32 AEVector4fComparison::AllAreSet( AEVector4ComparisonMask::Mask mMask ) const
{ 
	AEASSERT_MSG( ( mMask & 0xF ) == mMask, "illegal mask value handed in");
#if AE_SSE_VERSION >= 0x41
	__m128i comp = _mm_load_si128( (const __m128i*)&( AEVector4fComparison_maskToComparison[mMask*4] ) );
	__m128i mask = _mm_and_si128( _mm_castps_si128( m_mMask ), comp );
	return _mm_testc_si128( mask, comp );
#else
	return (_mm_movemask_ps( m_mMask ) & mMask ) == mMask; 
#endif
}

AE_FORCEINLINE AEBOOL32 AEVector4fComparison::AnyIsSet( AEVector4ComparisonMask::Mask mMask ) const
{ 
	AEASSERT_MSG( ( mMask & 0xF ) == mMask, "illegal mask value handed in");
	return _mm_movemask_ps( m_mMask ) & mMask; 
}

AE_FORCEINLINE AEBOOL32 AEVector4fComparison::AllAreSet( void ) const
{ 
#if AE_SSE_VERSION >= 0x41
	return _mm_test_all_ones( _mm_castps_si128( m_mMask ) );
#else
	return _mm_movemask_ps( m_mMask ) == MASK_XYZW; 
#endif
}

AE_FORCEINLINE AEBOOL32 AEVector4fComparison::AnyIsSet( void ) const
{ 
	return _mm_movemask_ps( m_mMask ); 
}

AE_FORCEINLINE AEVector4ComparisonMask::Mask AEVector4fComparison::GetMask( void ) const 
{ 
	return ( AEVector4ComparisonMask::Mask )_mm_movemask_ps(m_mMask); 
}

AE_FORCEINLINE AEVector4ComparisonMask::Mask AEVector4fComparison::GetMask( AEVector4ComparisonMask::Mask mMask ) const 
{ 
	AEASSERT_MSG( ( mMask & 0xF ) == mMask, "illegal mask value handed in");
	return (AEVector4ComparisonMask::Mask)( _mm_movemask_ps( m_mMask ) & mMask ); 
}

template <AEVector4ComparisonMask::Mask M>
AE_FORCEINLINE AEVector4ComparisonMask::Mask AEVector4fComparison::GetMask( void ) const 
{ 
	AE_VECTOR4FCOMPARISON_MASK_CHECK;
	return (AEVector4ComparisonMask::Mask)(_mm_movemask_ps( m_mMask ) & M ); 
}

AE_FORCEINLINE AEUINT32 AE_CALL GetCombinedMask( AEVector4fConstRefCompParam vcMask1, AEVector4fConstRefCompParam vcMask2, AEVector4fConstRefCompParam vcMask3 )
{
	return _mm_movemask_ps( vcMask1.m_mMask ) | ( _mm_movemask_ps( vcMask2.m_mMask ) << 4 ) | ( _mm_movemask_ps( vcMask3.m_mMask ) << 8 );
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalOr<1>( void ) const
{
	return AEVector4fComparison::Convert( _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 0, 0, 0, 0 ) ) );
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalOr<2>( void ) const
{
	return AEVector4fComparison::Convert( _mm_or_ps( _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE(0, 0, 0, 0 ) ),	// [x, x, x, x]
													 _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE(1, 1, 1, 1) ) ) );	// [y, y, y, y]
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalOr<3>( void ) const
{
	const AEVector4fMask xy	= _mm_or_ps( _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 0, 0, 0, 0 ) ),				// [x, x, x, x]
										 _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 1, 1, 1, 1 ) ) );				// [y, y, y, y]

	return AEVector4fComparison::Convert( _mm_or_ps( xy, _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 2, 2, 2, 2 ) ) ) );	// [z, z, z, z]
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalOr<4>( void ) const
{
	const AEVector4fMask or0 = _mm_or_ps( _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 1, 0, 3, 2 ) ),	// [y | x, x | y, w | z, z | w]
											m_mMask );
	const AEVector4fMask or1 = _mm_shuffle_ps( or0, or0, _MM_SHUFFLE( 2, 3, 0, 1 ) );			// [w | z, z | w, y | x, x | y]

	return AEVector4fComparison::Convert( _mm_or_ps( or0, or1 ) );
}

template <AEINT32 N>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalOr( void ) const
{
	AE_VECTOR4FCOMPARISON_SUBINDEX_CHECK;
	AEVector4fComparison cmp;
	cmp.Set<AEVector4ComparisonMask::MASK_NONE>();
	return cmp;
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalAnd<1>( void ) const
{
	return AEVector4fComparison::Convert( _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 0, 0, 0, 0 ) ) );
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalAnd<2>( void ) const
{
	return AEVector4fComparison::Convert( _mm_and_ps( _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 0, 0, 0, 0 ) ),	// [x, x, x, x]
													  _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 1, 1, 1, 1 ) ) ) );	// [y, y, y, y]
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalAnd<3>( void ) const
{
	const AEVector4fMask xy	= _mm_and_ps( _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 0, 0, 0, 0 ) ), // [x, x, x, x]
										  _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 1, 1, 1, 1 ) ) );    // [y, y, y, y]

	return AEVector4fComparison::Convert( _mm_and_ps( xy, _mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 2, 2, 2, 2 ) ) ) );	// [z, z, z, z]
}

template <>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalAnd<4>( void ) const
{
	const AEVector4fMask and0 = _mm_and_ps(	_mm_shuffle_ps( m_mMask, m_mMask, _MM_SHUFFLE( 1, 0, 3, 2 ) ), // [y & x, x & y, w & z, z & w]
												m_mMask );
	const AEVector4fMask and1 =	_mm_shuffle_ps( and0, and0, _MM_SHUFFLE( 2, 3, 0, 1 ) ); // [w & z, z & w, y & x, x & y]
	return AEVector4fComparison::Convert( _mm_and_ps( and0, and1 ) );
}

template <AEINT32 N>
AE_FORCEINLINE const AEVector4fComparison AEVector4fComparison::HorizontalAnd( void ) const
{
	//AE_VECTOR4FCOMPARISON_SUBINDEX_CHECK;
	AEVector4fComparison cmp;
	cmp.Set<AEVector4fComparison::MASK_NONE>();
	return cmp;
}

#endif // _AEVECTOR4FCOMPARISONSSE_INL_